from utils.misc import first


raise NotImplementedError("unfinished module")


class Collection(dict):
    __slots__ = ()

    def add(self, elem, *tags):
        """Add a new element to the collection and associate some tags to it, or, if the element
        was already in the collection, add the new tags to its tag set."""
        try:
            tag_set = self[elem]
        except KeyError:
            self[elem] = set(tags)
        else:
            tag_set.update(tags)

    get_tags = dict.__getitem__

    def filter_tags(self, *tags):
        result = CollectionSubset(self, tags)
        result.update(self)
        return result





class Filter(object):
    __slots__ = ()


class Intersection(list, Filter):
    __slots__ = ()

    def __call__(self, elem, collection):
        return all(f(elem, collection) for f in self)


class Union(list, Filter):
    __slots__ = ()

    def __call__(self, elem, collection):
        return any(f(elem, collection) for f in self)


class Negation(Filter):
    __slots__ = ("operand",)

    def __init__(self, operand):
        self.operand = operand

    def __call__(self, elem, collection):
        return not self.operand(elem, collection)


class HasTags(list, Filter):
    __slots__ = ()

    def __call__(self, elem, collection):
        return collection.get_tags(elem).issuperset(self)


class CollectionSubset(set):
    __slots__ = ("collection", "tags")

    def __init__(self, collection, tags):
        set.__init__(self)
        self.collection = collection
        self.tags = set(tags)

    def update(self, elems):
        tags_self = self.tags
        tag_map = self.collection
        set.update(self, (elem for elem in elems if tag_map[elem].issuperset(tags_self)))

    def filter(self, *tags):
        result = type(self)(self.collection, tags)
        result.update(self)
        result.tags.update(self.tags)
        return result

    def first(self):
        return first(self)

    def get_tags(self, elem):
        return self.collection[elem]


LOT_SIZING = "lot sizing"
SCHEDULING = "scheduling"
PARALLEL_MACHINES = "parallel machines"
SINGLE_MACHINE = "single machine"
CAPACITATED = "capacitated"
COMPACT_FORMULATION = "compact formulation"
EXTENDED_FORMULATION = "extended formulation"

ls = Collection()
ls.add("foobar", tags=(LOT_SIZING, SCHEDULING, PARALLEL_MACHINES, COMPACT_FORMULATION))
ls.add("foo", tags=(LOT_SIZING, PARALLEL_MACHINES))
ls.filter(PARALLEL_MACHINES, LOT_SIZING)
